﻿using UnityEngine;
using UnityEditor;
using System.Collections.Generic;

namespace CWM.Skinn
{
    [CustomEditor(typeof(TexturePacker))]
    [DisallowMultipleComponent]
    public class TexturePackerInspector : Editor
    {
        public Texture blankNormalMap = null;
        public Texture blankMap = null;

        public static Editor Inspector { get; private set; }
        [MenuItem("CONTEXT/TexturePacker/Edit Inspector Script", false, 10)]
        public static void Open(MenuCommand command)
        { if (Inspector) AssetDatabase.OpenAsset(MonoScript.FromScriptableObject(Inspector), 0); }
        
        public static class TPGUIContent
        {
            public static GUIContent Flatten = new GUIContent("Flatten", "generate uv islands.");
            public static GUIContent Layout = new GUIContent("Layout", "layout uv islands in a grid.");
        }

        public static readonly UVLayout SourceUV = UVLayout.Defualt;
        public static readonly UVLayout TargetUV = UVLayout.One;

        public void OnEnable() { Inspector = this; }

        public override void OnInspectorGUI()
        {
            DrawDefaultInspector();

            var context = target as TexturePacker;

            EditorGUILayout.LabelField(" ");
            EditorGUILayout.LabelField("");


            if (GUI.Button(GUILayoutUtility.GetLastRect(), TPGUIContent.Flatten))
            {
                if(context.transform.childCount > 0)
                {
                    if (EditorUtility.DisplayDialog("Texture Packer",
                        "Are you sure you want to Flatten uvs? Operation is not undo-able", "Flatten", "Cancel"
                        )) Flatten();
                }
                else Flatten();
            }

            EditorGUILayout.Space();
            EditorGUILayout.Space();

            EditorGUILayout.LabelField("");

            if (GUI.Button(GUILayoutUtility.GetLastRect(), TPGUIContent.Layout))
            {
                if (EditorUtility.DisplayDialog("Texture Packer",
                    "Are you sure you want to Layout uvs? Operation is not undo-able", "Layout", "Cancel"
                    )) Layout();
            }

            EditorGUILayout.Space();
            EditorGUILayout.Space();

            EditorGUILayout.LabelField("");

            if (GUI.Button(GUILayoutUtility.GetLastRect(), "Create Atlas"))
            {
                RenderAllTextures();
            }

            var showHelp = context.showHelp;

            if (showHelp)
            {
                EditorGUILayout.HelpBox("Instructions" + System.Environment.NewLine + System.Environment.NewLine +
                    "1) Assign a source. " + System.Environment.NewLine +
                    "2) Press Flatten. " + System.Environment.NewLine +
                     "3) Press Layout. Adjust uv islands as needed. " + System.Environment.NewLine +
                       "4) Press Create Atlas. " + System.Environment.NewLine +
                           "5) Save the Temp Renderer with your preferred method."
                     , MessageType.None);

                EditorGUILayout.HelpBox("Parent uv islands that share the same uv space.", MessageType.Info);

                EditorGUILayout.HelpBox("Reorder children to change position of there island during the layout.", MessageType.Info);

                EditorGUILayout.HelpBox("Use transform to tools to manipulate uv islands. Hold Control to snap to the grid.", MessageType.Info);

                EditorGUILayout.HelpBox("If a uv island appears to be missing the faces may be flipped you can use the Flip Faces command or any other commands/tools to edit the islands as long as the vertex count stays the same.", MessageType.Info);

                EditorGUILayout.HelpBox("Padding is only visible in the final textures.", MessageType.Info);

                EditorGUILayout.HelpBox("Use caution when creating 8192 textures." +
                    " Save the scene and close other to applications to avoid running out of memory.", MessageType.Info);

                EditorGUILayout.HelpBox("To save this game-object  create a new prefab and choose save unsaved meshes when prompted. keep track of your materials and source to use later on.", MessageType.Info);
            }

        }

        private void Layout()
        {
            var context = target as TexturePacker;
            var transform = context.transform;

            if (transform.childCount < 1) return;

            if (transform.childCount == 1)
            {
                var child = transform.GetChild(0);
                child.transform.localPosition = Vector3.zero;
                child.transform.localRotation = Quaternion.identity;
                child.transform.localScale = Vector3.one;
                return;
            }

            var grid = context.customGridSize > 0 ? context.customGridSize : transform.childCount.MaxGrid();
            var size = 1f / grid * 1f;
            var stepSize = size * 2f;
            var row = 0f;
            var column = 0f;
            var count = 0;

            var center = TexturePacker.Center - new Vector3(size * 50f, 0f, size * 50f);
            for (int i = 0; i < transform.childCount; i++)
            {
                Vector3 position;
                position.x = row - 1f + size;
                position.y = 0f;
                position.z = column - 1f + size;

                Vector3 scale;
                scale.x = size;
                scale.y = 1f;
                scale.z = size;

                var child = transform.GetChild(i);
                child.transform.localPosition = position * 50f + (center);
                child.transform.localRotation = Quaternion.identity;
                child.transform.localScale = scale;

                row += stepSize; count++;
                if (count >= grid) { count = 0; row = 0f; column += stepSize; }
            }
        }

        private void Split()
        {
            var context = target as TexturePacker;
            var transform = context.transform;

            var source = context.source;
            var active = source.gameObject.activeSelf;
            source.gameObject.SetActive(true);

            var mesh = source.GetSharedMesh();
            var createdObjects = new List<TexturePacker.RendererData>();
            var materials = source.GetSharedMaterialsFromSubMeshes();
            for (int i = 0; i < mesh.subMeshCount; i++)
            {
                var renderer = source.CloneAsMeshRenderer() as MeshRenderer;
                var name = source.name + " " + i.ToString();

                renderer.gameObject.name = name;
                int[] vertexMap;
                var newMesh = mesh.GetSubmesh(i, out vertexMap);
                renderer.SetSharedMesh(newMesh);

                if (!materials[i]) renderer.sharedMaterials = new Material[1] { null };
                else
                {
                    newMesh.name = materials[i].name;
                    renderer.gameObject.name = source.name + " " + materials[i].name;
                    renderer.sharedMaterials = new Material[1] { materials[i] };
                }
                createdObjects.Add(new TexturePacker.RendererData() { renderer = renderer, indices = vertexMap });
                renderer.gameObject.SetActive(true);
            }

            context.submeshes = createdObjects.ToArray();
            source.gameObject.SetActive(active);
            context.vertexCount = mesh.vertices.Length;
        }

        private static bool GetUV(Mesh source, UVLayout channel, out Vector2[] uvs, bool showFallbackWarning = false)
        {
            uvs = null;
            var availableChannel = -1;

            if (availableChannel < 0) availableChannel = SkinnEx.IsNullOrEmpty(source.uv) ? -1 : 0;
            if (availableChannel < 0) availableChannel = SkinnEx.IsNullOrEmpty(source.uv2) ? -1 : 1;
            if (availableChannel < 0) availableChannel = SkinnEx.IsNullOrEmpty(source.uv3) ? -1 : 2;
            if (availableChannel < 0) availableChannel = SkinnEx.IsNullOrEmpty(source.uv4) ? -1 : 3;

            if (availableChannel < 0)
            {
                return false;
            }

            uvs = null;
            switch (channel)
            {
                case UVLayout.Defualt:
                    uvs = source.uv;
                    break;
                case UVLayout.One:
                    uvs = source.uv2;
                    break;
                case UVLayout.Two:
                    uvs = source.uv3;
                    break;
                case UVLayout.Three:
                    uvs = source.uv4;
                    break;
                default:
                    break;
            }

            if (SkinnEx.IsNullOrEmpty(uvs))
            {
                if (showFallbackWarning)
                {
                    Debug.LogWarningFormat(
                     "Skinn: {0}'s mesh does not have uv channel {1} falling back to channel {2}.",
                     source.name,
                     ((int)channel).ToString(),
                     availableChannel);
                }

                switch (availableChannel)
                {
                    case 0:
                        uvs = source.uv;
                        break;
                    case 1:
                        uvs = source.uv2;
                        break;
                    case 2:
                        uvs = source.uv3;
                        break;
                    case 3:
                        uvs = source.uv4;
                        break;
                    default:
                        break;
                }
            }
            return true;
        }

        private void Flatten()
        {
            var context = target as TexturePacker;
            var transform = context.transform;

            var source = context.source;

            foreach (var child in transform.GetAllChildren()) if (child) SkinnEx.Release(child.gameObject);

            if (!source || !source.GetSharedMesh()) return;

            Vector2[] testUVs;
            if (!GetUV(source.GetSharedMesh(), SourceUV, out testUVs, true))
            {
                return;
            }

            var tempMesh = source.GetSharedMesh().Clone() as Mesh;

            tempMesh.normals = null;
            tempMesh.tangents = null;
            tempMesh.colors = null;
            tempMesh.boneWeights = null;
            tempMesh.ClearBlendShapes();

            var sharedMesh = source.GetSharedMesh();
            source.SetSharedMesh(tempMesh);

            Split();
            source.SetSharedMesh(sharedMesh);

            var submeshes = context.submeshes;
            foreach (var data in submeshes)
            {
                if (!data.renderer) continue;
                var submesh = data.renderer.GetSharedMesh();
                if (!submesh) continue;

                Vector2[] uvs = null;
                GetUV(submesh, SourceUV, out uvs);

                var vertices = new Vector3[submesh.vertices.Length];
                for (int i = 0; i < vertices.Length; i++)
                {
                    vertices[i] = new Vector3(Mathf.Repeat(uvs[i].x, 1f), 0f, Mathf.Repeat(uvs[i].y, 1f)) * 100f;
                }

                submesh.vertices = vertices;
                submesh.RecalculateNormals();
                submesh.RecalculateTangents();
                submesh.RecalculateBounds();
              
                data.renderer.transform.SetParent(transform);
            }

            SkinnEx.Release(tempMesh);

            Selection.activeGameObject = transform.gameObject;
            EditorGUIUtility.PingObject(transform.gameObject);

            var lastActiveSceneView = SceneView.lastActiveSceneView;
            if (lastActiveSceneView)
            {
                lastActiveSceneView.orthographic = true;
                lastActiveSceneView.FrameSelected();
                lastActiveSceneView.LookAt(Vector3.zero);
            }

            var used = new List<int>();
            for (int i = 0; i < submeshes.Length; i++)
            {
                var renderer = submeshes[i].renderer;
                if (!renderer) continue;
                if (used.Contains(i)) continue;
                for (int ii = 0; ii < submeshes.Length; ii++)
                {
                    if (i == ii) continue;
                    var subR = submeshes[ii].renderer;
                    if (!renderer) continue;

                    if (renderer.sharedMaterial == subR.sharedMaterial)
                    {
                        subR.transform.SetParent(renderer.transform);
                        used.Add(ii);
                    }
                    else if(renderer.sharedMaterial.GetTexture(context.uvIslandID) &&
                        renderer.sharedMaterial.GetTexture(context.uvIslandID) == subR.sharedMaterial.GetTexture(context.uvIslandID))
                    {
                        subR.transform.SetParent(renderer.transform);
                        used.Add(ii);
                    }
                }
            }
        }

        public void RenderAllTextures()
        {
            var context = target as TexturePacker;
            var source = context.source;
            if (!source || !source.GetSharedMesh()) return;
            if (!SkinnInternalAsset.Asset || !SkinnInternalAsset.Asset.lightMapAtlas) return;

            if(!blankMap || !blankNormalMap)
                Debug.LogWarning("Skinn : missing default texture(s) for the TexturePackerInspector.");

            var assetPath = EditorUtility.SaveFolderPanel("Export Atlas", "Assets/", "");
            if (string.IsNullOrEmpty(assetPath)) return;
            AssetDatabase.Refresh(); //check for new folder.
            if (assetPath.StartsWith(Application.dataPath)) assetPath = "Assets" + assetPath.Substring(Application.dataPath.Length);
            var atlasName = source.name + "_Atlas";
            var rootFolder = assetPath + "/" + atlasName;
            if (!AssetDatabase.IsValidFolder(rootFolder)) AssetDatabase.CreateFolder(assetPath, atlasName);
            var imageFolder = rootFolder + "/" + "Materials";
            if (!AssetDatabase.IsValidFolder(imageFolder)) AssetDatabase.CreateFolder(rootFolder, "Materials");

            Vector2[] uvs;
            if (!GetUV(source.GetSharedMesh(), SourceUV, out uvs))
            {
                return;
            }

            var bakeProps = context.bakeMaterialProperties;
            var renderQuality = context.atlasSize;
            var atlasSize = (int)renderQuality;
            var padTextures = context.padding;
            var padsize = context.paddingSize;
            var paddingInterations = context.paddingIterations;

            var submeshes = context.submeshes;

            foreach (var data in submeshes)
            {
                if (!data.renderer) continue;
                var submesh = data.renderer.GetSharedMesh();
                if (!submesh) continue;
                var vertices = submesh.vertices;
                var indices = data.indices;

                for (int i = 0; i < vertices.Length; i++)
                {
                    var vertex = data.renderer.transform.TransformPoint(vertices[i]) / 100f;
                    uvs[indices[i]] = new Vector2(vertex.x, vertex.z);
                }
            }

            var tempRenderer = source.CloneAsSkinnedMeshRenderer() as SkinnedMeshRenderer;

#if UNITY_2018_1_OR_NEWER
            if(source.gameObject.scene != null)
                tempRenderer.transform.SetParent(source.transform.parent);
#endif

            tempRenderer.gameObject.name += "_Atlas";
            tempRenderer.GetSharedMesh().uv2 = uvs;
            var shader = SkinnInternalAsset.Asset.lightMapAtlas;
            var renderMaterial = new Material(shader) { name = "uv", hideFlags = HideFlags.HideAndDontSave };
            var tempMesh = tempRenderer.GetSharedMesh();
            var materials = tempRenderer.GetSharedMaterialsFromSubMeshes();
            var matDictionary = new Dictionary<string, List<int>>();
            
            for (int i = 0; i < materials.Length; i++)
            {
                var material = materials[i]; if (!material) continue;
                string[] textureIDs = material.GetAllPropertiesWithTextures();
                for (int ii = 0; ii < textureIDs.Length; ii++)
                {
                    var property = textureIDs[ii];
                    if (matDictionary.ContainsKey(property))
                    {
                        List<int> mats = new List<int>();
                        matDictionary.TryGetValue(property, out mats);
                        matDictionary.Remove(property);
                        mats.Add(i);
                        matDictionary.Add(property, mats);
                    }
                    else matDictionary.Add(property, new List<int>(1) { i });
                }

                if (textureIDs.Length > 0) continue;
                var colorProp = material.GetColorProperty();
                if (string.IsNullOrEmpty(colorProp)) continue;

                if (matDictionary.ContainsKey(colorProp))
                {
                    List<int> mats = new List<int>();
                    matDictionary.TryGetValue(colorProp, out mats);
                    matDictionary.Remove(colorProp);
                    mats.Add(i);
                    matDictionary.Add(colorProp, mats);
                }
                else matDictionary.Add(colorProp, new List<int>(1) { i });
            }

            var tProptPath = new Dictionary<string, string>();

            foreach (KeyValuePair<string, List<int>> mat in matDictionary)
            {
                renderMaterial.SetFloat("_UseColor", 0f);
                renderMaterial.SetColor("_Color", Color.white);
                renderMaterial.SetTexture("_MainTex", null);
                renderMaterial.SetFloat("_ConvertBumpMap", 0f);
                renderMaterial.SetFloat("_ToggleUV", 0f);

                var exculdeR = System.Array.FindIndex(context.exculdeFromRenderer, (a) => { return mat.Key.Contains(a); });
                if (exculdeR != -1) continue;

                var texturePath = imageFolder + "/" + source.name + "_" + mat.Key + "_Atlas.png";
                var materialIndex = mat.Value;
              

                var atlasRTexture = new RenderTexture(atlasSize, atlasSize, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB) { };

                var tempRTexture = new RenderTexture(atlasSize, atlasSize, 0) { enableRandomWrite = true };
                tempRTexture.Create();

                var active = RenderTexture.active;
                RenderTexture.active = atlasRTexture;

                var isNormalMap = mat.Key.Contains("Bump") || mat.Key.Contains("Normal");
                renderMaterial.SetFloat("_ConvertBumpMap", isNormalMap ? 1 : 0);

                if (isNormalMap && blankNormalMap)
                {
                    renderMaterial.SetTexture("_MainTex", blankNormalMap);
                    renderMaterial.SetPass(0);
                    Graphics.DrawMeshNow(tempMesh, Matrix4x4.identity);
                    renderMaterial.SetTexture("_MainTex", null);
                }

                var sp = -1f;

                if(mat.Key == "_SpecGlossMap" && blankMap) sp = context.glossiness;
                if (mat.Key == "_MetallicGlossMap" && blankMap) sp = context.metallic;

                if (sp >= 0f)
                {
                    renderMaterial.SetTexture("_MainTex", blankMap);
                    renderMaterial.SetFloat("_UseColor", 1f);
                    renderMaterial.SetColor("_Color", new Color(sp, sp, sp, 1f));
                    renderMaterial.SetPass(0);
                    Graphics.DrawMeshNow(tempMesh, Matrix4x4.identity);
                    renderMaterial.SetFloat("_UseColor", 0f);
                    renderMaterial.SetColor("_Color", Color.white);
                    renderMaterial.SetTexture("_MainTex", null);
                }

                for (int i = 0; i < materialIndex.Count; i++)
                {
                    var material = materials[materialIndex[i]];
                    
                    if (material.GetColorProperty() == mat.Key)
                    {
                        renderMaterial.SetFloat("_UseColor", 1);

                        var propColor = material.GetColor(mat.Key);
                        propColor.a = Mathf.Clamp(propColor.a, 1e-8f, 1);
                        renderMaterial.SetColor("_Color", propColor);
                        renderMaterial.SetTexture("_MainTex", null);

                    }
                    else
                    {
                        if (bakeProps)
                        {
                            var offest = material.GetTextureOffset(mat.Key);
                            var scale = material.GetTextureScale(mat.Key);

                            renderMaterial.SetTextureOffset("_MainTex", offest);
                            renderMaterial.SetTextureOffset("_MainTex", scale);
                        }

                        if (bakeProps &&  mat.Key == "_MainTex" && material.HasProperty("_Color"))
                        {
                            renderMaterial.SetFloat("_UseColor", 1);
                            renderMaterial.SetColor("_Color", material.GetColor("_Color"));
                            renderMaterial.SetTexture("_MainTex", material.GetTexture(mat.Key));
                        }
                        else if (bakeProps && mat.Key == "_EmissionMap" && material.HasProperty("_EmissionColor"))
                        {
                            renderMaterial.SetFloat("_UseColor", 1);
                            var emissionColor = material.GetColor("_EmissionColor");
                            emissionColor.a = 1;
                            renderMaterial.SetColor("_Color", emissionColor);
                            renderMaterial.SetTexture("_MainTex", material.GetTexture(mat.Key));
                        }
                        else
                        {
                            renderMaterial.SetFloat("_UseColor", 0);
                            renderMaterial.SetColor("_Color", Color.white);
                            renderMaterial.SetTexture("_MainTex", material.GetTexture(mat.Key));
                        }
                        
                    }

                    renderMaterial.SetPass(0);
                    Graphics.DrawMeshNow(tempMesh, Matrix4x4.identity, materialIndex[i]);
                    Graphics.DrawMeshNow(tempMesh, Matrix4x4.identity, materialIndex[i]);
                }

                var paddingPixelSize = 2.1f;
                switch (renderQuality)
                {
                    case RenderQuality.x64:
                        paddingPixelSize *= 0.125f;
                        break;
                    case RenderQuality.x128:
                        paddingPixelSize *= 0.25f;
                        break;
                    case RenderQuality.x512:
                        paddingPixelSize *= 0.5f;
                        break;
                    case RenderQuality.x1024:
                        paddingPixelSize *= 1f;
                        break;
                    case RenderQuality.x2048:
                        paddingPixelSize *= 4f;
                        break;
                    case RenderQuality.x4096:
                        paddingPixelSize *= 8f;
                        break;
                    case RenderQuality.x8192:
                        paddingPixelSize *= 16f;
                        break;
                    default:
                        break;
                }

                var exculdeP = System.Array.FindIndex(context.exculdeFromPadding, (a) => { return mat.Key.Contains(a); });
                if (exculdeP == -1 && padTextures)
                {
                    for (int i = 0; i < paddingInterations; i++)
                        PadTexture(atlasRTexture, tempRTexture, (paddingPixelSize * padsize) + (i * 0.0001f));
                }

                var format = TextureFormat.ARGB32;
                var newTexture = new Texture2D(atlasSize, atlasSize, format, false, true);
                newTexture.ReadPixels(new Rect(0, 0, atlasSize, atlasSize), 0, 0);
                newTexture.Apply(false, false);
                var bytes = newTexture.EncodeToPNG();
                if (System.IO.File.Exists(texturePath)) AssetDatabase.DeleteAsset(texturePath);
                System.IO.File.WriteAllBytes(texturePath, bytes);

                RenderTexture.active = active;
                SkinnEx.Release(newTexture);
                SkinnEx.Release(atlasRTexture);
                SkinnEx.Release(tempRTexture);

                tProptPath.Add(mat.Key, texturePath);
            }

            SkinnEx.Release(renderMaterial);

            if (matDictionary.Count < 1) SkinnContext.LogWarning("No Textures Found", true);

            tempMesh.uv = tempRenderer.GetSharedMesh().uv2;
            tempMesh.uv2 = null;

            var sharedMaterials = tempRenderer.GetSharedMaterialsFromSubMeshes();
            var maxMaterialIndex = -1;
            var maxPropertyCount = -1;
            for (int i = 0; i < sharedMaterials.Length; i++)
            {
                var propertyCount = 0;
                if (!sharedMaterials[i]) continue;
                foreach (var item in tProptPath) if (sharedMaterials[i].HasProperty(item.Key)) propertyCount++;
                if (propertyCount > maxPropertyCount) maxMaterialIndex = i;
            }

            if (maxMaterialIndex > -1)
            {
                AssetDatabase.Refresh();

                var combinedMat = new Material(sharedMaterials[maxMaterialIndex].shader) { name = atlasName };
                foreach (var keyword in sharedMaterials[maxMaterialIndex].shaderKeywords) combinedMat.EnableKeyword(keyword);

                foreach (var item in tProptPath)
                {
                    var texture = (Texture)AssetDatabase.LoadAssetAtPath(item.Value, typeof(Texture));
                    if (!texture) continue;
                    if (combinedMat.GetColorProperty() == item.Key)
                    {
                        if (!tProptPath.ContainsKey("_MainTex") && combinedMat.HasProperty("_MainTex")) combinedMat.SetTexture("_MainTex", texture);
                    }
                    else if (combinedMat.HasProperty(item.Key)) combinedMat.SetTexture(item.Key, texture);

                    var textureImporter = TextureImporter.GetAtPath(item.Value) as TextureImporter;
                    if (!textureImporter) continue;

                    if (item.Key.Contains("Bump") || item.Key.Contains("Normal"))
                    {
                        textureImporter.textureType = TextureImporterType.NormalMap;
                    }
                    else
                    {
                        textureImporter.alphaIsTransparency = true;
                    }
                    textureImporter.maxTextureSize = Mathf.Clamp(atlasSize, 32, 8192);
                    textureImporter.mipmapEnabled = false;

                    AssetDatabase.WriteImportSettingsIfDirty(item.Value);
                    AssetDatabase.ImportAsset(item.Value, ImportAssetOptions.ForceUncompressedImport);
                }

                for (int i = 0; i < sharedMaterials.Length; i++) sharedMaterials[i] = combinedMat;
                tempRenderer.sharedMaterials = sharedMaterials;

                AssetDatabase.CreateAsset(combinedMat, imageFolder + "/" + combinedMat.name + ".mat");
            }
            else tempRenderer.sharedMaterials = new Material[tempMesh.subMeshCount];

            if (context.tempRenderer)
            {
                SkinnEx.Release(context.tempRenderer.GetSharedMesh());
                SkinnEx.Release(context.tempRenderer.gameObject);
            }
            context.tempRenderer = tempRenderer;
            AssetDatabase.Refresh();
        }

        private static void PadTexture(RenderTexture source, RenderTexture temp, float size = 1.1f)
        {
            ComputeShader shader = SkinnInternalAsset.Asset.texturePadding;
            var kernal = shader.FindKernel("AlphaPadding");
            shader.SetTexture(kernal, "Source", source);
            shader.SetTexture(kernal, "Destination", temp);
            shader.SetFloat("Size", size);
            shader.Dispatch(kernal, (temp.width + 7) / 8, (temp.height + 7) / 8, 1);
            Graphics.Blit(temp, source);
        }

        private static void PadTextureEdgeBlending(RenderTexture source, RenderTexture temp, float size = 1.1f)
        {
            ComputeShader shader = SkinnInternalAsset.Asset.texturePadding;
            var kernal = shader.FindKernel("EdgeBlending");
            shader.SetTexture(kernal, "Source", source);
            shader.SetTexture(kernal, "Destination", temp);
            shader.SetFloat("Size", size);
            shader.Dispatch(kernal, (temp.width + 7) / 8, (temp.height + 7) / 8, 1);
            Graphics.Blit(temp, source);
        }
    }
}